Winform MDI窗体切换不闪烁的解决办法(测试通过)

MDI窗体不闪烁方法测试通过:

1
2
3
4
5
//.net 4.0用OptimizedDoubleBuffer

this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);

this.UpdateStyles();

真正有效的方法:在最上层窗体加上

1
2
3
4
5
6
7
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; /* Turn on WS_EX_COMPOSITED */
return(cp);
}
}

在下层的窗体和自定义控件加上

1
2
3
4
5
6
7
8
9
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.Style &= ~0x02000000; /* Turn off WS_CLIPCHILDREN */
return(cp);
}
}

注意:如果加错地方或人品不好,某些时候可能会造成控件绘制略微不正常。

如果人品爆发的话,貌似在下层窗体直接加 cp.Style &= ~0x02000000 就行,不需要在上层窗体加 cp.ExStyle |= 0x02000000;

注意下层窗体代码在ListBox或者ListView的Anchor设有Right,且窗体BackColor与控件背景不同的时候,可能会发现控件初始化显示不正常。需要做一下MdiParent.Refresh或者取消Right;

引用MSDN中对CreateParams的说明:

在你开发的重载控件中不要重写这个属性,通过这个属性控制控件的某些风格。只有在你封装Windows控件或者想实现某些WinForm没有提供的风格(比如Layered Window)控制的时候再使用这个属性。更多信息请参照MSDN上对CreateWindow方法和CreateWindowEx方法的参数CREATESTRUCT结构体的文档注释。

简述为何CreateParams能够实现这样高级的样式控制,因为从CreateWindow和CreateWindowEx的名字就可以看出,CreateParam是传递给这俩个方法的参数,而这两个方法又是在窗体创建的时候调用的。所以,CreateParam才能够实现如此强大的样式控制。

节点更新要使用 BeginUpdate 和 EndUpdate

这一对操作对于需要批量操作更新控件的情景有比较好的效果,比如初始化时批量添加了大量节点。坏处就在于不能即时更新。所以,对于频繁的更新节点并希望立即反映到界面的情况不适用。如果使用并且没有禁掉清除界面消息的话,则控件看起来就会不停的闪烁,而且以白底为主,内容几乎不可见(这个视频繁程度而定)。因为界面更新都在EndUpdate处完成,操作太多导致EndUpdate阻塞时间过长,且清空在先,更新在后,导致界面看起来长时间处于空白状态。

某些情况下可以使用禁止背景更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
protected override void WndProc( ref Message m )
{
if ( m.Msg == 0x0014 )
return; /* 禁掉清除背景消息 */
base.WndProc( ref m );
}


public ListViewNF()
{
/* 开启双缓冲 */
this.SetStyle( ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true );

/* Enable the OnNotifyMessage event so we get a chance to filter out */
/* Windows messages before they get to the form's WndProc */
this.SetStyle( ControlStyles.EnableNotifyMessage, true );
}

protected override void OnNotifyMessage( Message m )
{
/* Filter out the WM_ERASEBKGND message */
if ( m.Msg != 0x14 )
{
base.OnNotifyMessage( m );
}
}


// 采 用LockWindowUpdate API

[DllImport( "user32.dll" )]

static extern bool LockWindowUpdate( IntPtr hWndLock );

LockWindowUpdate( panelContainer.Handle );

/* Clear Panel */
panelContainer.Controls.Clear();

/* my temporary TextBox */
TextBox myT;

for ( int lauf = 0; lauf < 200; lauf++ )
{
/* Create New TextBox */
myT = new TextBox();
/* Add TextBox to the Panel */
panelContainer.Controls.Add( myT );
}

/* redraw the window */
LockWindowUpdate( IntPtr.Zero );

frmChild1.Hide(); /* 隐藏当前显示的子窗体2 3 */
LockWindowUpdate( this.Handle ); /* 锁定父窗体4 */
frmChild2.Show(); /* 显示窗体等其他需要再显示前做的事5 */
LockWindowUpdate( IntPtr.Zero ); /* 解锁父窗体6 */
RedrawWindow( this.Handle, IntPtr.Zero, IntPtr.Zero, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN );
/* (0x04 | 0x01 | 0x80)立即强制重绘父窗体及其所有子窗体效果好转,但人眼还能看到一些花屏现象,仍不能一次全部完整显示。 */

使用Windows API中的SendMessage函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/* 拦截控件重绘 */
class DrawingControl
{
[DllImport( "user32.dll" )]

public static extern int SendMessage( IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam );

private const int WM_SETREDRAW = 11;

public static void SuspendDrawing( Control parent )
{
SendMessage( parent.Handle, WM_SETREDRAW, false, 0 );
}

public static void ResumeDrawing( Control parent )
{
SendMessage( parent.Handle, WM_SETREDRAW, true, 0 );
parent.Refresh();
}
}

/* 禁止窗体中的绘制操作 */
frmChild1.Hide();
SendMessage( this.Handle, WM_SETDRAW, false, null );

/* 显示窗体等其他需要再显示前做的事 */
frmChild2.Show();
SendMessage( this.Handle, WM_SETDRAW, true, null );

/* 解除禁止绘制操作 */
RedrawWindow( this.Handle, IntPtr.Zero, IntPtr.Zero, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN ); /* (0x04 | 0x01 | 0x80)立即强制重绘父窗体及其所有子窗 */

SendMessage函数中,发送消息 WM_SETREDRAW,设置SETREDRAW为FALSE,导致窗口不进行绘制。

此时,看到的窗体是假的,现象:鼠标形状是后面应用程序的形状;鼠标划过,后面的应用程序就显示出来了。人眼看到的就是“花屏”。

关注作者公众号,获取更多资源!
赏作者一杯咖啡~